package com.example.utils.cos;

import com.alibaba.fastjson.JSONObject;
import com.example.utils.cos.sts.CosStsClient;
import com.example.utils.oss.YHUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.auth.COSSigner;
import com.qcloud.cos.model.GetObjectRequest;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.utils.VersionInfoUtils;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/*
 * 描述  腾讯云对象存储
 *
 * @author gx
 * @date 2022/1/7 10:17:10
 * @version 1.0
 *  推到云服务
 *  FileInputStream fis = new FileInputStream("/tmp/buildNewImgFile/buildNewImg.jpg");
 *  return：存在对象存储的url     (文件名+后缀,流)
 *  String resultOssUrl = cosComponent.postObject(UUIDUtil.getUUID()+".jpg", fis);
 */
@Component
public class COSComponent {

    @Value("${tencent.cloud.secretId}")
    private String secretId;
    @Value("${tencent.cloud.secretKey}")
    private String secretKey;
    @Value("${tencent.cloud.bucketName}")
    private String bucketName;
    @Value("${tencent.cloud.endpoint}")
    private String endpoint;

    private static final Pattern pattern = Pattern.compile("(https|http)://(.*?).cos.(.*?).myqcloud.com");

    public JSONObject getCredential() {
        TreeMap<String, Object> config = new TreeMap<>();

        try {
            // 替换为您的 SecretId
            config.put("SecretId", secretId);
            // 替换为您的 SecretKey
            config.put("SecretKey", secretKey);

            // 临时密钥有效时长，单位是秒，默认1800秒，目前主账号最长2小时（即7200秒），子账号最长36小时（即129600秒）
            config.put("durationSeconds", 1800);

            // 换成您的 bucket
            config.put("bucket", bucketName);
            // 换成 bucket 所在地区
            config.put("region", "ap-shanghai");

            // 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，例子：a.jpg 或者 a/* 或者 * 。
            // 如果填写了“*”，将允许用户访问所有资源；除非业务需要，否则请按照最小权限原则授予用户相应的访问权限范围。
            config.put("allowPrefix", "*");

            // 密钥的权限列表。简单上传、表单上传和分片上传需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
            String[] allowActions = new String[]{
                    // 简单上传
                    "name/cos:PutObject",
                    // 表单上传、小程序上传
                    "name/cos:PostObject",
                    // 分片上传
                    "name/cos:InitiateMultipartUpload",
                    "name/cos:ListMultipartUploads",
                    "name/cos:ListParts",
                    "name/cos:UploadPart",
                    "name/cos:CompleteMultipartUpload"
            };
            config.put("allowActions", allowActions);

            JSONObject credential = CosStsClient.getCredential(config);
            //成功返回临时密钥信息，如下打印密钥信息
//            System.out.println(credential);
            return credential;
        } catch (Exception e) {
            //失败抛出异常
            throw new IllegalArgumentException("no valid secret !");
        }
    }


    /**
     * 从OSS服务中下载所需文件到本地临时文件
     *
     * @param basePath 本地临时文件夹
     * @param fileUrls 要下载的对象/文件，在OSS上的URL地址
     * @return
     */
    public void getObjects(String basePath, List<String> fileUrls, List<String> fileNames) {
        if (YHUtil.collectionsIsEmpty(fileUrls) || YHUtil.collectionsIsEmpty(fileNames)) {
            return;
        }
        COSClient cosclient = null;
        try {

            // 初始化用户身份信息(secretId, secretKey)
            COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
            // 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
            ClientConfig clientConfig = new ClientConfig(new Region("ap-shanghai"));
            boolean useTrafficLimit = false;
            // 生成cos客户端
            cosclient = new COSClient(cred, clientConfig);
            for (int i = 0; i < fileUrls.size() && i < fileNames.size(); i++) {
                String fileUrl = fileUrls.get(i);
                if (!isCOSFile(fileUrl)) {
                    continue;
                }
                String key = filterHttp(fileUrl);

                String fileName = fileNames.get(i);
                //指定文件保存路径
                String filePath = basePath;
                //判断文件目录是否存在，不存在则创建
                File file = new File(filePath);
                if (!file.exists()) {
                    file.mkdirs();
                }

                GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);
                if (useTrafficLimit) {
                    getObjectRequest.setTrafficLimit(8 * 1024 * 1024);
                }
                ObjectMetadata objectMetadata = cosclient.getObject(getObjectRequest, file);
                System.out.println(objectMetadata.getContentLength());
            }
        } finally {
            if (cosclient != null) {
                cosclient.shutdown();
            }
        }

    }

    public void getObjectToFile(String key, File localFile) {
        key = filterHttp(key);
        COSClient cosclient = null;
        try {

            // 初始化用户身份信息(secretId, secretKey)
            COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
            // 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
            ClientConfig clientConfig = new ClientConfig(new Region("ap-shanghai"));
            // 生成cos客户端
            cosclient = new COSClient(cred, clientConfig);
            boolean useTrafficLimit = false;
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);
            if (useTrafficLimit) {
                getObjectRequest.setTrafficLimit(8 * 1024 * 1024);
            }
            ObjectMetadata objectMetadata = cosclient.getObject(getObjectRequest, localFile);
            System.out.println(objectMetadata.getContentLength());
        } finally {
            if (cosclient != null) {
                cosclient.shutdown();
            }
        }
    }

    public InputStream getObject(String key) throws IOException {
        Matcher m = pattern.matcher(key);

        String _bucketName = null;
        String _region = null;
        if (m.find()) {
            _bucketName = m.group(2);
            _region = m.group(3);
        }


        key = filterHttp(key);


        COSClient cosclient = null;
        try {

            // 初始化用户身份信息(secretId, secretKey)
            COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
            // 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
            ClientConfig clientConfig = new ClientConfig(new Region(_region != null ? _region : "ap-shanghai"));
            // 生成cos客户端
            cosclient = new COSClient(cred, clientConfig);

            InputStream objectContent = cosclient.getObject(_bucketName != null ? _bucketName : bucketName, key).getObjectContent();

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int len;
            byte[] buf = new byte[1024];
            while ((len = objectContent.read(buf)) != -1) {
                baos.write(buf, 0, len);
            }
            return new ByteArrayInputStream(baos.toByteArray());


        } finally {
            if (cosclient != null) {
                cosclient.shutdown();
            }
        }
    }

    private String filterHttp(String key) {
        if (key.startsWith("http")) {
            try {
                URL url = new URL(key);
                key = url.getPath();
                if (key.startsWith("/")) {
                    key = key.substring(1);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
        return key;
    }

    public String postObject(String key, MultipartFile file) throws IOException {
        String contentType = file.getContentType();
        InputStream in = file.getInputStream();
        return postObject(key, contentType, in);
    }

    public String postObject(String key, String fileUrl) throws IOException {
        String contentType = ContentTypeUtils.getContentType(fileUrl);
        File file = new File(fileUrl);
        DataInputStream in = new DataInputStream(new FileInputStream(file));
        return postObject(key, contentType, in);
    }

    public String postObject(String key, InputStream in) throws IOException {
        String contentType = FileContentTypeUtils.contentType(key);
        return postObject(key, contentType, in);
    }

    public String postObject(String key, String contentType, InputStream in) throws IOException {
        String filename = key.substring(key.lastIndexOf("/") + 1);

        long startTimestamp = System.currentTimeMillis() / 1000;
        long endTimestamp = startTimestamp + 30 * 60;
        String endTimestampStr = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(endTimestamp * 1000);

        String keyTime = startTimestamp + ";" + endTimestamp;

        String boundary = "----WebKitFormBoundaryZBPbaoYE2gqeB21N";
        // 设置表单的body字段值
        Map<String, String> formFields = new HashMap<>();
        formFields.put("q-sign-algorithm", "sha1");
        formFields.put("key", key);
        formFields.put("q-ak", secretId);
        formFields.put("q-key-time", keyTime);

        // 构造policy，参考文档: https://cloud.tencent.com/document/product/436/14690
        String policy = "{\n" +
                "    \"expiration\": \"" + endTimestampStr + "\",\n" +
                "    \"conditions\": [\n" +
                "        { \"bucket\": \"" + bucketName + "\" },\n" +
                "        { \"q-sign-algorithm\": \"sha1\" },\n" +
                "        { \"q-ak\": \"" + secretId + "\" },\n" +
                "        { \"q-sign-time\":\"" + keyTime + "\" }\n" +
                "    ]\n" +
                "}";

        // policy需要base64后算放入表单中
        String encodedPolicy = new String(Base64.encodeBase64(policy.getBytes()));

        // 设置policy
        formFields.put("policy", encodedPolicy);

        // 根据编码后的policy和secretKey计算签名
        COSSigner cosSigner = new COSSigner();
        String signature = cosSigner.buildPostObjectSignature(secretKey, keyTime, policy);

        // 设置签名
        formFields.put("q-signature", signature);

        // 根据以上表单参数，构造最开始的body部分
        String formBody = buildPostObjectBody(boundary, formFields, filename, contentType);

        HttpURLConnection conn = null;
        try {
            String urlStr = "http://" + bucketName + "." + endpoint;
            URL url = new URL(urlStr);
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("User-Agent", VersionInfoUtils.getUserAgent());
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            OutputStream out = new DataOutputStream(conn.getOutputStream());
            // 写入表单的最开始部分
            out.write(formBody.getBytes());
            // 将文件内容写入到输出流中
            int readBytes;
            byte[] bytes = new byte[4096];
            while ((readBytes = in.read(bytes)) != -1) {
                out.write(bytes, 0, readBytes);
            }
            in.close();

            // 添加最后一个分割符，行首和行尾都是--
            byte[] endData = ("\r\n--" + boundary + "--\r\n").getBytes();
            out.write(endData);
            out.flush();
            out.close();
            List<String> values = conn.getHeaderFields().get("Location");

            if (YHUtil.collectionsIsEmpty(values)) {
                return null;
            } else {
//                String content = values.get(0);
                String content = unescape(values.get(0));
                url = new URL(content);
                return "https" + content.substring(url.getProtocol().length());
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    private String buildPostObjectBody(String boundary, Map<String, String> formFields, String filename, String contentType) {
        StringBuffer stringBuffer = new StringBuffer();
        for (Map.Entry entry : formFields.entrySet()) {
            // 添加boundary行,行首以--开头
            stringBuffer.append("--").append(boundary).append("\r\n");
            // 字段名
            stringBuffer.append("Content-Disposition: form-data; name=\""
                    + entry.getKey() + "\"\r\n\r\n");
            // 字段值
            stringBuffer.append(entry.getValue() + "\r\n");
        }
        // 添加boundary行,行首以--开头
        stringBuffer.append("--").append(boundary).append("\r\n");
        // 文件名
        stringBuffer.append("Content-Disposition: form-data; name=\"file\"; "
                + "filename=\"" + filename + "\"\r\n");
        // 文件类型
        stringBuffer.append("Content-Type: " + contentType + "\r\n\r\n");
        return stringBuffer.toString();
    }

    public void deleteObject(String key) {
        // TODO
    }


    public static String unescape(String s) {
        StringBuffer sbuf = new StringBuffer();
        int l = s.length();
        int ch = -1;
        int b, sumb = 0;
        for (int i = 0, more = -1; i < l; i++) {
            /* Get next byte b from URL segment s */
            switch (ch = s.charAt(i)) {
                case '%':
                    ch = s.charAt(++i);
                    int hb = (Character.isDigit((char) ch) ? ch - '0' : 10 + Character.toLowerCase((char) ch) - 'a') & 0xF;
                    ch = s.charAt(++i);
                    int lb = (Character.isDigit((char) ch) ? ch - '0' : 10 + Character.toLowerCase((char) ch) - 'a') & 0xF;
                    b = (hb << 4) | lb;
                    break;
                case '+':
                    b = ' ';
                    break;
                default:
                    b = ch;
            }
            /* Decode byte b as UTF-8, sumb collects incomplete chars */
            if ((b & 0xc0) == 0x80) { // 10xxxxxx (continuation byte)
                sumb = (sumb << 6) | (b & 0x3f); // Add 6 bits to sumb
                if (--more == 0)
                    sbuf.append((char) sumb); // Add char to sbuf
            } else if ((b & 0x80) == 0x00) { // 0xxxxxxx (yields 7 bits)
                sbuf.append((char) b); // Store in sbuf
            } else if ((b & 0xe0) == 0xc0) { // 110xxxxx (yields 5 bits)
                sumb = b & 0x1f;
                more = 1; // Expect 1 more byte
            } else if ((b & 0xf0) == 0xe0) { // 1110xxxx (yields 4 bits)
                sumb = b & 0x0f;
                more = 2; // Expect 2 more bytes
            } else if ((b & 0xf8) == 0xf0) { // 11110xxx (yields 3 bits)
                sumb = b & 0x07;
                more = 3; // Expect 3 more bytes
            } else if ((b & 0xfc) == 0xf8) { // 111110xx (yields 2 bits)
                sumb = b & 0x03;
                more = 4; // Expect 4 more bytes
            } else /* if ((b & 0xfe) == 0xfc) */ { // 1111110x (yields 1 bit)
                sumb = b & 0x01;
                more = 5; // Expect 5 more bytes
            }
            /* We don't test if the UTF-8 encoding is well-formed */
        }
        return sbuf.toString();
    }
    /*
     * 描述  判断是腾讯云(cos)还是阿里云(oss)
     *
     * @param sourceUrl
     * @return boolean
     * @author gx
     * @date 2022/1/7 10:25:39
     * @version 1.0
     */
    private boolean isCOSFile(String sourceUrl) {
        if (sourceUrl.startsWith("http")) {
            try {
                URL url = new URL(sourceUrl);
                if (url.getHost().contains("aliyuncs.com")) {
                    return false;
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
        return true;
    }
    public static void main(String[] args) throws Exception {
        COSComponent cos = new COSComponent();
        cos.secretId = "AKID7xXruhTyKQFn57Mz3sQdkGVfYBYfe4jF";
        cos.secretKey = "PYFQLDQN4xhFYaPK1SikW2HGNEqCHWTB";
        cos.bucketName = "bucket1-1301272873";
        cos.endpoint = "cos.ap-shanghai.myqcloud.com";


        String key = "测试中文.jpg";
        String fileUrl = "D:/测试中文.jpg";
        String url = cos.postObject(key, fileUrl);
        System.out.println(url);

//        File localFile = new File("ff38051d2fa843ff93f98791373d633e.docx");
//        cos.getObjectToFile(key, localFile);

//        String url = cos.postObject(key, "D://01-05 - 副本 - 副本.jpg");
//        System.out.println(url);

//        JSONObject json = cos.getCredential();
//        System.out.println(json);


//        String content = "http://bucket5-1301272873.cos.ap-chengdu.myqcloud.com/jar/http/marking-consumer-0.0.1-SNAPSHOT.jar";
//
//        URL url = new URL(content);
//        url.getProtocol();
//        System.out.println("https" + content.substring(url.getProtocol().length()));

    }

}
